// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.properties;

import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentWithBrowseButton;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiType;
import com.intellij.ui.EditorTextField;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxICell;
import com.neuronbit.businessflow.intellij.dialog.FlowReturnFieldValueSourceTreeListChooseDialog;
import com.neuronbit.businessflow.intellij.editor.swing.editor.RadComponent;
import com.neuronbit.businessflow.intellij.editor.swing.editor.RadFlowComponent;
import com.neuronbit.businessflow.intellij.editor.swing.editor.RadJavaMethodComponent;
import com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.InplaceContext;
import com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.Property;
import com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.PropertyEditor;
import com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.PropertyRenderer;
import com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector.renderers.LabelPropertyRenderer;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * The type Field value to bind property.
 */
public final class FlowReturnTypeFieldProperty extends Property<RadJavaMethodComponent, String> {
    private final PropertyRenderer myRenderer;
    private final MyEditor myEditor;
    private final Project project;
    private final PsiType fieldType;
    private final String fieldName;

    /**
     * Instantiates a new Field value to bind property.
     *
     * @param parent    the parent
     * @param project   the project
     * @param fieldName the field name
     * @param fieldType the field type
     * @param name      the name
     */
    public FlowReturnTypeFieldProperty(FlowReturnTypeProperty parent, Project project, String fieldName, PsiType fieldType, String name) {
        super(parent, name+":"+ ReadAction.compute(fieldType::getPresentableText)) ;
        this.project = project;
        this.fieldType = fieldType;
        this.fieldName = fieldName;
        myRenderer = new LabelPropertyRenderer();
        myEditor = new MyEditor(project);
    }

    @Override
    public PropertyEditor<String> getEditor() {
        return myEditor;
    }

    @Override
    @NotNull
    public PropertyRenderer<String> getRenderer() {
        return myRenderer;
    }

    private String getFullFieldName() {
        Property parent = getParent();
        List<String> names = new ArrayList<>(3);
        while (parent != null) {
            String name = parent.getName();
            if (name.contains(":")) {
                names.add(StringUtils.strip(StringUtils.substringBefore(name, ":")));
            } else {
                names.add(StringUtils.strip(name));
            }
            parent = parent.getParent();
        }
        Collections.reverse(names);
        names.add(StringUtils.strip(fieldName));

        return String.join(".", names);
    }

    @Override
    public String getValue(final RadJavaMethodComponent component) {
        return component.getReturnValue(getFullFieldName());
        //return component.getClassToBind();
    }

    @Override
    protected void setValueImpl(final RadJavaMethodComponent component, final String value) {
        component.setReturnValue(getFullFieldName(), value);
//        String className = value;
//
//        if (className != null && className.length() == 0) {
//            className = null;
//        }
//
//        component.setClassToBind(className);
    }

    private final class MyEditor extends PropertyEditor<String> {
        private final EditorTextField myEditorTextField;
        private final ComponentWithBrowseButton<EditorTextField> myTfWithButton;
        private Document myDocument;
        private String myInitialValue;
        private final Project myProject;
        private final MyActionListener myActionListener;

        /**
         * Instantiates a new My editor.
         *
         * @param project the project
         */
        MyEditor(final Project project) {
            myProject = project;
            myEditorTextField = new EditorTextField("", project, JavaFileType.INSTANCE) {
                @Override
                protected boolean shouldHaveBorder() {
                    return false;
                }
            };
            myActionListener = new MyActionListener();
            myTfWithButton = new ComponentWithBrowseButton<>(myEditorTextField, myActionListener);
            myEditorTextField.setBorder(null);
            new MyCancelEditingAction().registerCustomShortcutSet(CommonShortcuts.ESCAPE, myTfWithButton);
      /*
      myEditorTextField.addActionListener(
        new ActionListener() {
          public void actionPerformed(final ActionEvent e) {
            fireValueCommitted();
          }
        }
      );
      */
        }

        @Override
        public String getValue() {
            final String value = myDocument.getText();
            if (value.length() == 0 && myInitialValue == null) {
                return null;
            }
            return value.replace('$', '.'); // PSI works only with dots
        }

        @Override
        public JComponent getComponent(final RadComponent component, final String value, final InplaceContext inplaceContext) {
            myInitialValue = value;
            setEditorText(value != null ? value : "");
            myActionListener.setComponent(component);
            return myTfWithButton;
        }

        private void setEditorText(final String s) {
//            final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(myProject);
//            PsiPackage defaultPackage = JavaPsiFacade.getInstance(myProject).findPackage("");
//            final PsiCodeFragment fragment = factory.createReferenceCodeFragment(s, defaultPackage, true, true);
//            myDocument = PsiDocumentManager.getInstance(myProject).getDocument(fragment);
            myDocument = new DocumentImpl(s);
            myEditorTextField.setDocument(myDocument);
        }

        @Override
        public void updateUI() {
            SwingUtilities.updateComponentTreeUI(myTfWithButton);
        }

        private final class MyActionListener implements ActionListener {
            /**
             * The My component.
             */
            RadComponent myComponent;

            /**
             * Sets component.
             *
             * @param component the component
             */
            public void setComponent(RadComponent component) {
                myComponent = component;
            }

            @Override
            public void actionPerformed(final ActionEvent e) {
                FlowReturnFieldValueSourceTreeListChooseDialog dialog = new FlowReturnFieldValueSourceTreeListChooseDialog(myComponent.getProject(),
                        //TODO shunyun 2024/4/16: 这里的强转会导致复用的可能性降低，同时有可能有问题，因为泛型的类型与这里的类型不匹配
                        ((mxGraphModel) ((RadFlowComponent) myComponent).getGraph().getModel())
                                .getCells()
                                .values()
                                .stream()
                                .filter(o -> o instanceof mxCell && ((mxCell) o).isVertex())
                                .map(mxICell.class::cast)
                                .toList(),
                        fieldName,
                        fieldType,
                        myEditorTextField.getText());
                if (dialog.showAndGet()) {
                    List<String> result = dialog.getSelected();
                    if (result != null) {
                        setEditorText(String.join(",", result));
                    }
                }
//                final String className = myEditorTextField.getText();
//        final PsiClass aClass = FormEditingUtil.findClassToBind(myComponent.getModule(), className);
//
//        final Project project = myComponent.getProject();
//        final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
//        final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project).createWithInnerClassesScopeChooser(
//          UIDesignerBundle.message("title.choose.class.to.bind"),
//          GlobalSearchScope.projectScope(project),
//          new ClassFilter() { // we need show classes from the sources roots only
//            @Override
//            public boolean isAccepted(final PsiClass aClass) {
//              final VirtualFile vFile = aClass.getContainingFile().getVirtualFile();
//              return vFile != null && fileIndex.isUnderSourceRootOfType(vFile, JavaModuleSourceRootTypes.SOURCES);
//            }
//          },
//          aClass
//        );
//        chooser.showDialog();
//
//        final PsiClass result = chooser.getSelected();
//        if (result != null) {
//          setEditorText(result.getQualifiedName());
//        }

                // todo[anton] make it via providing proper parent
                IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(myEditorTextField, true));
            }
        }

        private final class MyCancelEditingAction extends AnAction {
            @Override
            public void actionPerformed(@NotNull final AnActionEvent e) {
                fireEditingCancelled();
            }
        }
    }
}
